package com.qf.controller;

import com.qf.captcha.CaptchaCodeManager;
import com.qf.pojo.DtsAdmin;
import com.qf.service.AdminService;
import com.qf.service.PermissionService;
import com.qf.service.RoleService;
import com.qf.util.*;

import com.qf.util.Base64;
import com.qf.util.UUID;
import com.sun.corba.se.impl.oa.toa.TOA;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayOutputStream;
import java.io.Serializable;
import java.util.*;

/**
 * 管理员认证接口
 * @author zhaojian
 */
@RestController
@RequestMapping("/auth")
@CrossOrigin("*")
public class AdminAuthController {

    public static final Logger logger = LoggerFactory.getLogger(AdminAuthController.class);

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    @Autowired
    private AdminService adminService;

    @Autowired
    private ApplicationContext context;

    private HashMap<String, String> systemPermissionsMap = null;

    /**
     * 获取验证码
     * @return
     */
    @GetMapping("/captchaImage")
    public Object getCode() throws Exception {
        //1. 生成随机字符串
        String verifyCode = VerifyCodeUtils.generateVerifyCode(4);

        //2. 生成验证码唯一标识, 唯一ID
        String uuid = UUID.randomUUID().toString();

        //3. 将验证码存储到缓存中
        boolean flag = CaptchaCodeManager.addToCache(uuid, verifyCode, 10);

        //4. 判断验证码是否存储到缓存成功
        if (!flag) {
            logger.error("存储验证码到缓存失败:{}", AdminResponseCode.AUTH_CAPTCHA_ERROR.desc());
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }

        //5. 生成图片
        int w = 111;
        int h = 36;
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        VerifyCodeUtils.outputImage(w, h, stream, verifyCode);

        //6. 封装返回给前端的数据
        HashMap<String, Object> data = new HashMap<>();
        data.put("uuid", uuid);
        data.put("img", Base64.encode(stream.toByteArray()));

        //7. 将图片唯一id和图片内容返回
        return ResponseUtil.ok(data);
    }

    /**
     * 登录controller方法
     * @param body
     * @return
     */
    @RequestMapping("/login")
    public Object login(@RequestBody String body) {
        //1. 从传入的json字符串中获取参数数据
        String username = JacksonUtil.parseString(body, "username");
        String password = JacksonUtil.parseString(body, "password");
        String code = JacksonUtil.parseString(body, "code");
        String uuid = JacksonUtil.parseString(body, "uuid");

        //2. 判断参数是否为空
        if (StringUtils.isEmpty(username) ||
                StringUtils.isEmpty(password) ||
                StringUtils.isEmpty(code) ||
                StringUtils.isEmpty(uuid)) {
            return ResponseUtil.badArgument();
        }

        //3. 验证码是否正确
        String cachedCaptcha = CaptchaCodeManager.getCachedCaptcha(uuid);
        if (cachedCaptcha == null) {
            logger.error("系统管理 -> 用户登录, 异常:{}", AdminResponseCode.AUTH_CAPTCHA_EXPIRED.desc());
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
        }
        if (!code.equalsIgnoreCase(cachedCaptcha)) {
            logger.error("系统管理 -> 用户登录, 用户验证码输入错误:{}", AdminResponseCode.AUTH_CAPTCHA_ERROR.desc());
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }

        //4. 调用shiro的subject对象的login登录方法
        Subject subject = SecurityUtils.getSubject();

        try {
            subject.login(new UsernamePasswordToken(username, password));
        } catch (UnknownAccountException e) {
            logger.error("系统管理 -> 用户登录, 用户名错误:{}", AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD.desc());
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        } catch (LockedAccountException e) {
            logger.error("系统管理 -> 用户登录, 错误:{}", AdminResponseCode.ADMIN_LOCK_ACCOUNT.desc());
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_LOCK_ACCOUNT);
        } catch (AuthenticationException e) {
            logger.error("系统管理 -> 用户登录, 错误:{}", AdminResponseCode.ADMIN_LOCK_ACCOUNT.desc());
            return AdminResponseUtil.fail(AdminResponseCode.ADMIN_LOCK_ACCOUNT);
        }

        //返回用户唯一id
        Serializable id = subject.getSession().getId();
        System.out.println("============" + id);
        return ResponseUtil.ok(id);
    }

    /**
     * 用户登录后, 获取用户对应的角色和权限信息返回
     * @return
     */
    @RequiresAuthentication  //限制这个方法必须是登录后的用户才可以访问
    @RequestMapping("/info")
    public Object info() {
        //1. 从shiro框架中获取当前登录用户对象
        Subject subject = SecurityUtils.getSubject();
        String userName = (String)subject.getPrincipal();
        List<DtsAdmin> list = adminService.findAdminByUserName(userName);
        DtsAdmin admin = list.get(0);

        //2. 获取用户名和头像
        Map<String, Object> data = new HashMap<>();
        data.put("name", admin.getUsername());
        data.put("avatar", admin.getAvatar());

        //3. 获取当前用户的角色id数组
        Integer[] roleIds = admin.getRoleIds();

        //4. 获取当前用户的角色名字集合
        Set<String> roleNameSet = roleService.queryRolesByIds(roleIds);

        //5. 获取当前用户的权限字符串集合
        Set<String> permSet = permissionService.findPermByRoleIds(roleIds);

        //6. 将permission权限集合转换成页面需要的结构
        data.put("roles", roleNameSet);
        data.put("perms", toAPI(permSet));

        //7. 返回
        return ResponseUtil.ok(data);
    }

    /**
     * 注销, 退出
     * @return
     */
    @RequiresAuthentication
    @RequestMapping("/logout")
    public Object logout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return ResponseUtil.ok();
    }

    /**
     * 将权限字符串集合转换成api形式的权限集合
     * @param permissions
     * @return
     */
    private Collection<String> toAPI(Set<String> permissions) {
        if (systemPermissionsMap == null) {
            systemPermissionsMap = new HashMap<>();
            //final String basicPackage = "com.qiguliuxing.dts.admin";
            final String basicPackage = "com.qf.admin";
            List<Permission> systemPermissions = PermissionUtil.listPermission(context, basicPackage);
            for (Permission permission : systemPermissions) {
                String perm = permission.getRequiresPermissions().value()[0];
                String api = permission.getApi();
                systemPermissionsMap.put(perm, api);
            }
        }

        Collection<String> apis = new HashSet<>();
        for (String perm : permissions) {
            String api = systemPermissionsMap.get(perm);
            apis.add(api);

            if (perm.equals("*")) {
                apis.clear();
                apis.add("*");
                return apis;
                // return systemPermissionsMap.values();
            }
        }
        return apis;
    }
}
